package mole 
{
	import mole.entities.*;
	import mole.entities.characters.*;
	import mole.entities.controlers.*;
	import mole.entities.items.*;
	import mole.entities.triggers.*;
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import net.flashpunk.Entity;
	import net.flashpunk.masks.Grid;
	import net.flashpunk.World;
	import flash.events.TimerEvent;
    import flash.utils.Timer;
	import net.flashpunk.FP;
	import flash.utils.ByteArray;
	
	/**
	 * Gestion du monde.
	 * @author Cédric Liaudet
	 */
	public class CWorld extends World
	{		
		[Embed(source = '../../bin/data/level_0.xml', mimeType = "application/octet-stream")]
		static public const LEVEL_BASE:Class;
		
		[Embed(source = '../../bin/data/level_0/level_0_1.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_0_1:Class;
		[Embed(source = '../../bin/data/level_0/level_0_2.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_0_2:Class;
		[Embed(source = '../../bin/data/level_0/level_0_3.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_0_3:Class;
		[Embed(source = '../../bin/data/level_0/level_0_4.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_0_4:Class;
		[Embed(source = '../../bin/data/level_0/level_0_5.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_0_5:Class;
		[Embed(source = '../../bin/data/level_0/level_0_6.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_0_6:Class;
		[Embed(source = '../../bin/data/level_0/level_1_1.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_1_1:Class;
		[Embed(source = '../../bin/data/level_0/level_1_2.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_1_2:Class;
		[Embed(source = '../../bin/data/level_0/level_1_3.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_1_3:Class;
		[Embed(source = '../../bin/data/level_0/level_1_4.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_1_4:Class;
		[Embed(source = '../../bin/data/level_0/level_1_5.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_1_5:Class;
		[Embed(source = '../../bin/data/level_0/level_1_6.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_1_6:Class;
		[Embed(source = '../../bin/data/level_0/level_2_1.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_2_1:Class;
		[Embed(source = '../../bin/data/level_0/level_2_2.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_2_2:Class;
		[Embed(source = '../../bin/data/level_0/level_2_3.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_2_3:Class;
		[Embed(source = '../../bin/data/level_0/level_2_4.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_2_4:Class;
		[Embed(source = '../../bin/data/level_0/level_2_5.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_2_5:Class;
		[Embed(source = '../../bin/data/level_0/level_2_6.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_2_6:Class;
		[Embed(source = '../../bin/data/level_0/level_3_1.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_3_1:Class;
		[Embed(source = '../../bin/data/level_0/level_3_2.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_3_2:Class;
		[Embed(source = '../../bin/data/level_0/level_3_3.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_3_3:Class;
		[Embed(source = '../../bin/data/level_0/level_3_4.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_3_4:Class;
		[Embed(source = '../../bin/data/level_0/level_3_5.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_3_5:Class;
		[Embed(source = '../../bin/data/level_0/level_3_6.tmx', mimeType = 'application/octet-stream')]
		static public const LEVEL_3_6:Class;		
		
		/**
		 * Constructor.
		 */
		public function CWorld() 
		{			
			m_fGravity = 300.0;
			m_Tilemap = null;
			
			m_aTemplates = new Array();
			for (var i:int = 0; i < 2; i++)
			{
				m_aTemplates.push(new Array());
			}
		}
		
		/**
		 * Chargement d'un niveau de jeu.
		 * @param	_Path Chemin du fichier a charger.
		 */
		public function LoadLevel(_Path:String, _Listener:Function):void
		{
			m_sCurrentLevel = _Path;
			m_ReadyFunc = _Listener;
			
			// Chargement du fichier XML.
			//var request:URLRequest = new URLRequest(m_sCurrentLevel);
			//var XMLLoader:URLLoader = new URLLoader(request);
			//XMLLoader.addEventListener("complete", OnBaseXMLLoaded);
			
			var levels:Array = new Array;
			levels.push(LEVEL_0_1);
			levels.push(LEVEL_0_2);
			levels.push(LEVEL_0_3);
			levels.push(LEVEL_0_4);
			levels.push(LEVEL_0_5);
			levels.push(LEVEL_0_6);
			levels.push(LEVEL_1_1);
			levels.push(LEVEL_1_2);
			levels.push(LEVEL_1_3);
			levels.push(LEVEL_1_4);
			levels.push(LEVEL_1_5);
			levels.push(LEVEL_1_6);
			levels.push(LEVEL_2_1);
			levels.push(LEVEL_2_2);
			levels.push(LEVEL_2_3);
			levels.push(LEVEL_2_4);
			levels.push(LEVEL_2_5);
			levels.push(LEVEL_2_6);
			levels.push(LEVEL_3_1);
			levels.push(LEVEL_3_2);
			levels.push(LEVEL_3_3);
			levels.push(LEVEL_3_4);
			levels.push(LEVEL_3_5);
			levels.push(LEVEL_3_6);			

			var sFullPath:String = m_sCurrentLevel.substr(0, m_sCurrentLevel.length - 4) + "/";
						
			m_nSemaphore = 0;
			var difficulty:int = 0;
			for each(var templ:Class in levels)
			{
				if (templ == LEVEL_1_1 || templ == LEVEL_1_2|| templ == LEVEL_1_3|| templ == LEVEL_1_4|| templ == LEVEL_1_5|| templ == LEVEL_1_6)
					difficulty = 1;
				else if (templ == LEVEL_2_1|| templ == LEVEL_2_2|| templ == LEVEL_2_3|| templ == LEVEL_2_4|| templ == LEVEL_2_5|| templ == LEVEL_2_6)
					difficulty = 2;
				else if (templ == LEVEL_3_1|| templ == LEVEL_3_2|| templ == LEVEL_3_3|| templ == LEVEL_3_4|| templ == LEVEL_3_5|| templ == LEVEL_3_6)
					difficulty = 3;
				else
					difficulty = 0;
					
				var template:CTemplate = new CTemplate();
				if (m_aTemplates.length <= difficulty)
					m_aTemplates.push(new Array());
					
				m_aTemplates[difficulty].push(template);
				m_nSemaphore++;
				
				// Lecture du fichier XML.
				var file:ByteArray = new templ;
				var str:String = file.readUTFBytes( file.length );
				var xml:XML = new XML( str );
				
				template.OnXMLLoadedd(xml);
				
				// Chargement du fichier XML.
				//var request:URLRequest = new URLRequest(sFullPath + templ.@name);
				//XMLLoader = new URLLoader(request);
				//XMLLoader.addEventListener("complete", template.OnXMLLoaded);
			}
			
			m_ReadyFunc();
		}
		
		/**
		 * Callback lorsque le fichier de base est lu.
		 * @param	_Event
		 */
		private function OnBaseXMLLoaded(_Event:Event):void
		{
			var XMLLoader:URLLoader = _Event.currentTarget as URLLoader;
			var xml:XML = XML(XMLLoader.data);
			var sFullPath:String = m_sCurrentLevel.substr(0, m_sCurrentLevel.length - 4) + "/";
						
			m_nSemaphore = 0;
			for each(var templ:XML in xml.template)
			{
				var template:CTemplate = new CTemplate();
				if (m_aTemplates.length <= templ.@difficulty)
					m_aTemplates.push(new Array());
					
				m_aTemplates[templ.@difficulty].push(template);
				m_nSemaphore++;
				
				// Chargement du fichier XML.
				var request:URLRequest = new URLRequest(sFullPath + templ.@name);
				XMLLoader = new URLLoader(request);
				XMLLoader.addEventListener("complete", template.OnXMLLoaded);
			}
						
			var checkLoading:Timer = new Timer(250, m_nSemaphore);
			checkLoading.addEventListener(TimerEvent.TIMER_COMPLETE, OnCheckLoading);
			checkLoading.start();
		}
		
		/**
		 * 
		 * @param	_Event
		 */
		public function OnCheckLoading(_Event:TimerEvent):void
		{
			// on vérifie si c'est chargé !
			if (m_nSemaphore <= 0)
			{
				m_ReadyFunc();
				//var x:int;
				//x = 0;
				//for each(var templatess:CTemplate in m_aTemplates[0])
				//{
				//	var tilemap:CTileMap = templatess.CreateTilemap(x, 0);
				//	x += tilemap.grid.width;
				//}
			}
		}
		
		/**
		 * 
		 */
		public function OnTemplateReady():void
		{
			m_nSemaphore--;
		}
		
		/**
		 * Spawn d'un personnage.
		 * @param	_Name
		 * @param	_PosX
		 * @param	_PosY
		 */
		public function OnLoadSpawn(_Name:String, _PosX:Number, _PosY:Number, _iWidth:int, _iHeight:int, _Properties:XMLList):void
		{					
			var entity:Entity = null;

			// creation d'un item
			if (_Name.length > 6 && _Name.substr(0, 6) == "item_")
			{
				var item:String = _Name.substr(6);
				AddItem(item, _PosX, _PosY);
			}
			
			// on ajoute l'entité si elle n'y a pas eu d'erreur.
			if (entity)
				add(entity);
		}
		
		/**
		 * Ajout d'un trigger
		 * @param	_Name Type du trigger
		 * @param	_PosX Position sur l'axe des x
		 * @param	_PosY Position sur l'axe des y.
		 */
		public function OnLoadTrigger(_Name:String, _PosX:Number, _PosY:Number, _iWidth:int, _iHeight:int, _Properties:XMLList):void
		{
			var entity:Entity = null;
			
			// on récupére les propriétés.
			var bOnce:Boolean = false;
			var aTypes:Array = null;
			for each(var property:XML in _Properties.property)
			{
				if (property.@name == "once")
					bOnce = (property.@value == "true");
				else if (property.@name == "types")
				{
					var value:String = property.@value;
					aTypes = value.split(", ");
				}
			}
				
			// Point de respawn.
			if (_Name == "respawn")
			{				
				// Création du trigger.
				entity = new CRespawn(bOnce, null, aTypes, _iWidth, _iHeight, _PosX, _PosY);
			}
			// Porte
			else if (_Name.length > 4 && _Name.substr(0, 4) == "door")
			{
				var destination:String = null;
				for each(property in _Properties.property)
				{
					if (property.@name == "destination")
					{
						destination = property.@value;	
						break;
					}
				}
				
				// Création de la porte.
				entity = new CDoor(bOnce, null, aTypes, _iWidth, _iHeight, _Name, destination);
				entity.x = _PosX;
				entity.y = _PosY;
			}
			// Echelle
			else if (_Name == "ladder")
			{
				// Création de l'échelle.
				entity = new CLadder(bOnce, null, aTypes, _iWidth, _iHeight);
				entity.x = _PosX;
				entity.y = _PosY;
			}
			
			// on ajoute l'entité si elle n'y a pas eu d'erreur.
			if (entity)
				add(entity);
		}
		
		/**
		 * Remove all entities.
		 */
		public function ReleaseLevel():void
		{
			removeAll();
		}
		
		/**
		 * Retrieves current gravity applied in the world.
		 * @return Current gravity.
		 */
		public function  get gravity():Number { return m_fGravity; }
		
		/**
		 * Snap l'entité sur le sol.
		 * @param _Entity The entity to snap to the floor.
		 */
		public function SnapToFloor(_Entity:Entity):void
		{
			if (_Entity)
			{
				// on récupére la cellule (angle haut/gauche)
				var iXCell:uint = _Entity.x / 32;
				var iYCell:uint = _Entity.y / 32;
				
				// on cherche la cellule physique la plus proche vers le bas.
				var grid:Grid = m_Tilemap.grid;
				if (grid)
				{
					for (var iY:uint = iYCell; iY < grid.rows; iY++)
					{
						if (grid.getCell(iXCell, iY))
						{
							_Entity.y = (iY * 32) - _Entity.height;
							break;
						}
					}
				}
			}
		}
		
		/**
		 * Add a new item at the specified position.
		 * @param	_sName Type of the item.
		 * @param	_PosX Position on the X-Axis.
		 * @param	_PosY Position on the Y-Axis.
		 */
		public function AddItem(_sName:String, _PosX:Number, _PosY:Number):void
		{
			var entity:Entity = null;
			
			if (_sName == "necklace")
			{
				entity = new CNecklace(_PosX, _PosY, 0);
			}
			
			if (entity)
			{
				add(entity);
				SnapToFloor(entity);
			}
		}
		
		/**
		 * 
		 */
		public function CreateTilemap(_nDifficulty:int, _nPosX:int, _bImpossible:Boolean):CTileMap
		{
			if (m_nSemaphore == 0)
			{
				if (_nDifficulty >= m_aTemplates.length)
					_nDifficulty = m_aTemplates.length - 1;
					
				var template:CTemplate = FP.choose(m_aTemplates[_nDifficulty]);
				var tilemap:CTileMap = template.CreateTilemap(_nPosX, 0, _bImpossible);
				
				return tilemap;
			}
			
			return null;
		}
		
		private var m_fGravity:Number;		///< Gravity of the world.
		private var m_sCurrentLevel:String; ///< Le nom du niveau actuel.
		private var m_Tilemap:CTileMap;     ///< La carte de jeu.
		
		private var m_aTemplates:Array;		///< Tableau de templates.
		
		private var m_nSemaphore:int;		///< verrou de chargement.
		private var m_ReadyFunc:Function;
	}

}